﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using EuroManager.Domain.Simulator.Engine;
using EuroManager.Domain.Simulator.Helpers;
using EuroManager.Domain.Simulator.Result;
using EuroManager.Domain.Simulator.World;

namespace EuroManager.Domain.Simulator
{
    public class GameSimulator
    {
        private static readonly int fps = 40;
        private static readonly int ballFps = 20;
        private static readonly int playerFps = 4;

        private IGameWorldFactory gameWorldFactory;

        public GameSimulator(IGameWorldFactory gameWorldFactory)
        {
            this.gameWorldFactory = gameWorldFactory;
        }

        public GameData Play()
        {
            IGameWorld game = gameWorldFactory.CreateGameWorld();
            GameData result = new GameData();

            foreach (IPlayer player in game.Players)
            {
                result.AddPlayer(GetTeam(game, player), player.Number);
            }

            int frameCount = PlayGame(game, result);

            result.FrameCount = frameCount;
            result.Fps = fps;
            result.BallFps = ballFps;
            result.PlayerFps = playerFps;
            ////result.Score1 = game.Score1;
            ////result.Score2 = game.Score2;

            return result;
        }

        private int PlayGame(IGameWorld game, GameData result)
        {
            ////int score1 = game.Score1;
            ////int score2 = game.Score2;
            TimeSpan gameTime = game.CurrentTime;

            float frameSeconds = 1.0f / fps;
            int frameIndex = 0;

            while (!game.IsFinished)
            {
                game.Update(frameSeconds);

                WriteFrame(game, result, frameIndex);

                while (gameTime < game.CurrentTime)
                {
                    result.UpdateTime(frameIndex);
                    gameTime += TimeSpan.FromSeconds(1);
                }

                ////while (score1 < game.Score1)
                ////{
                ////    result.UpdateScore(GameTeam.Team1, frameIndex);
                ////    score1++;
                ////    AddHighlight(result, frameIndex);
                ////}

                ////while (score2 < game.Score2)
                ////{
                ////    result.UpdateScore(GameTeam.Team2, frameIndex);
                ////    score2++;
                ////    AddHighlight(result, frameIndex);
                ////}

                frameIndex++;
            }

            return frameIndex;
        }

        private void WriteFrame(IGameWorld game, GameData result, int frameIndex)
        {
            if (frameIndex % (fps / ballFps) == 0)
            {
                result.Ball.AddFrame(game.Ball.X, game.Ball.Y);
            }

            if (frameIndex % (fps / playerFps) == 0)
            {
                foreach (IPlayer player in game.Players)
                {
                    float angle = CalculateRotation(player.Heading);
                    result.AddPlayerFrame(GetTeam(game, player), player.Number, player.X, player.Y, angle);
                }
            }
        }

        private float CalculateRotation(Vector2D heading)
        {
            float dotProduct = Vector2D.Dot(heading, new Vector2D(0, -1));
            float rotation = Maths.Acos(Maths.MinMax(-1, dotProduct, 1));

            if (heading.X < 0)
            {
                rotation = -rotation;
            }

            return rotation * (180 / Maths.Pi);
        }

        private GameTeam GetTeam(IGameWorld game, IPlayer player)
        {
            return player.Team == game.Team1 ? GameTeam.Team1 : GameTeam.Team2;
        }

        ////private void AddHighlight(GameData result, int frameIndex)
        ////{
        ////    result.AddHighlight(frameIndex - fps * 10, frameIndex + fps * 3);
        ////}
    }
}
